package com.atguigu.gmall.item.lock;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author lfy
 * @Description
 * @create 2023-08-21 14:08
 *
 * JUC里面提供的所有锁，分布式情况都不能用；
 * 1、可重入
 * 2、公平锁
 * 3、CountDownLatch
 * 4、读写锁
 * ....
 * JUC所有的锁功能，分布式都要再做一套； Redisson， jackson
 *
 *
 *
 */
@Component
public class RedisLock {


    @Autowired
    StringRedisTemplate redisTemplate;

    public void lock(String uuid) {
//        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", "1");
//        while (!redisTemplate.opsForValue().setIfAbsent("lock", "1")){
//            //自旋抢锁
//            try {
//                TimeUnit.MILLISECONDS.sleep(50);
//            } catch (InterruptedException e) {
//                throw new RuntimeException(e);
//            }
//        }
//
//        //加锁成功： 给锁一个过期时间，防止由于业务断电，导致无法解锁，从而引起死锁问题
//        redisTemplate.expire("lock",10,TimeUnit.SECONDS);

        //【占坑+设置锁的过期时间】应该是一个原子操作； 一步到位；
        //set lock 1 EX 10 NX； 抢锁是一个原子操作；
        //加锁的时候使用和线程绑定好的uuid，防止未来删除别人的锁
        while (!redisTemplate.opsForValue().setIfAbsent("lock", uuid, 10, TimeUnit.SECONDS)) {
            try {
                TimeUnit.MILLISECONDS.sleep(50);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

    }

    public void unlock(String token) {
        //临界情况下的；删除别人锁的问题； 删锁需要保证 查值+对比+删除 是一个原子操作
//        String lock = redisTemplate.opsForValue().get("lock");
//        if(token.equals(lock)){
//            redisTemplate.delete("lock");
//        }else {
//            //说明发现是别人的锁了，啥也别干;
//        }

        //lua脚本，默认能保证原子性，但是能保证事务性？
        String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                "then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";

        List<String> keys = Arrays.asList("lock");
        redisTemplate.execute(new DefaultRedisScript<>(script,Long.class),keys,token);


    }
}
